#include "catch2/catch_test_macros.hpp"
#include <fmt/format.h>
#include "shmobj.hpp"
#include <iostream>
#include <string_view>
#include <filesystem>

namespace fs = std::filesystem;

TEST_CASE("create a shmobj", "[shmobj]") {
  constexpr std::string_view shmname = "test_shmobj1";
  double data = 3.14;

  ShmObj shm1(shmname, &data, sizeof(double));
  REQUIRE(shm1.data_size() == sizeof(double));
  REQUIRE(shm1.ref_count() == 1);
  REQUIRE(shm1.data() != static_cast<void*>(&data));
  REQUIRE(*static_cast<double*>(shm1.data()) == data);
  REQUIRE(shm1.total_size() == 56 + sizeof(double));
}

TEST_CASE("shmobj can be destroyed", "[shmobj]") {
  constexpr std::string_view shmname = "test_shmobj1";
  double data = 3.1415926;
  {
    ShmObj shm1(shmname,  &data, sizeof(double));
  }
  REQUIRE_FALSE(fs::exists(fmt::format("/dev/shm/{}", shmname)));
}

TEST_CASE("shmobj can be attached", "[shmobj]") {
  constexpr std::string_view shmname = "test_shmobj1";
  double data = 3.14;
  ShmObj shm1(shmname, &data, sizeof(double));
  {
    ShmObj shm2(shmname);
    REQUIRE(shm1.ref_count() == 2);
    REQUIRE(shm2.ref_count() == 2);
    REQUIRE(shm1.data_size() == sizeof(double));
    REQUIRE(shm2.data_size() == sizeof(double));
    REQUIRE(*static_cast<double*>(shm1.data()) == data);
    REQUIRE(*static_cast<double*>(shm2.data()) == data);
  }
  REQUIRE(fs::exists(fmt::format("/dev/shm/{}", shmname)));
  
  REQUIRE(shm1.ref_count() == 1);
}

TEST_CASE("shmobj data can be assigned laster", "[shmobj]") {
  constexpr std::string_view shmname = "test_shmobj1";
  double data = 3.14;
  ShmObj shm1(shmname, nullptr, sizeof(double));
  {
    ShmObj shm2(shmname);
    *static_cast<double*>(shm2.data()) = data;
    REQUIRE(*static_cast<double*>(shm1.data()) == data);
    REQUIRE(*static_cast<double*>(shm2.data()) == data);
  }
  REQUIRE(fs::exists(fmt::format("/dev/shm/{}", shmname)));
  
  REQUIRE(shm1.ref_count() == 1);
}

TEST_CASE("shmobj creator can be delete first", "[shmobj]") {
  constexpr std::string_view shmname = "test_shmobj1";
  double data = 3.14;
  auto shm1 = new ShmObj(shmname, &data, sizeof(double));
  ShmObj shm2(shmname);
  delete shm1;

  REQUIRE(fs::exists(fmt::format("/dev/shm/{}", shmname)));
  REQUIRE(shm2.ref_count() == 1);
}